package com.cycplus.test.ui

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.PorterDuff
import android.graphics.PorterDuffXfermode
import android.graphics.Rect
import android.graphics.RectF
import android.os.Handler
import android.os.Looper
import android.util.AttributeSet
import android.util.Log
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.ScaleGestureDetector
import android.view.View
import android.widget.OverScroller
import androidx.core.math.MathUtils.clamp
import com.cycplus.test.ui.VideoThumbnailLoader.IFrameThumbLoaded
import com.cycplus.test.R
import com.cycplus.test.ui.VideoThumbnailTrackView.DragType.LEFT_THUMB
import com.cycplus.test.ui.VideoThumbnailTrackView.DragType.NONE
import com.cycplus.test.ui.VideoThumbnailTrackView.DragType.RIGHT_THUMB
import com.cycplus.test.utils.ScreenUtil
import kotlin.math.ceil
import kotlin.math.max
import kotlin.math.roundToInt

class VideoThumbnailTrackView : View, IFrameThumbLoaded {
    private val TAG = "VideoThumbnailTrackView"
    private var THUMBNAIL_WIDTH = 100
    private var THUMBNAIL_HEIGHT = 100

    private val bgColor = Color.parseColor("#161616")

    private var placeholderBitmap: Bitmap = Bitmap.createBitmap(THUMBNAIL_WIDTH, THUMBNAIL_WIDTH, Bitmap.Config.ARGB_8888).apply {
        eraseColor(bgColor)
    }

    private val refreshHandler = Handler(Looper.getMainLooper())
    private var refreshPending = false

    private var visibleWidth = 0 // 可见区域宽度
    private var videoDurationSec = 0f // 视频总时长（秒）
    private var thumbnailCount = 0f // 总共多少张缩略图
    private var pixelsPerSecond: Float = 0f //一秒占多少像素
    private var scrollOffsetPx: Float = 0f // 滚动偏移量（像素）

    private val scroller = OverScroller(context)
    private var isScaling = false
    private var isFlinging = false
    private var isScrolling = false
    private var hasDispatchedScrollStart = false
    private var hasDispatchedScrollEnd = true

    private var zoomPivotTimeSec = 0f //缩放中心点对应时间
    private var lastScaleEndTime = 0L // 缩放结束的时间戳
    private val SCROLL_BLOCK_DURATION = 500L // 缩放结束后阻止滑动的持续时间（毫秒）
    private val gestureDetector = GestureDetector(context, GestureListener())
    private val scaleDetector = ScaleGestureDetector(context, ScaleListener())

    private val thumbnailRect = RectF()
    private val mPaint = Paint(Paint.ANTI_ALIAS_FLAG)

    private var mLinePaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)
    private var mTextPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)
    private var textHeight: Float = 0f
    private var enableText: Boolean = true // 是否显示时间

    private var dpSize4 = 0
    private var radius = 0f
    private var offset = 0
    private var checkedBitmapSize = 30
    private val checkedBitmap by lazy {
        BitmapFactory.decodeResource(context.resources, R.drawable.icon_checked)
    }

    private val highLightClickRect = RectF()
    private var highLightActionWidth = 0
    private var highLightBitmapSize = 0
    private val addHighLightActionBitmap by lazy {
        BitmapFactory.decodeResource(context.resources, R.drawable.icon_add_highlight)
    }

    private val deleteHighLightActionBitmap by lazy {
        BitmapFactory.decodeResource(context.resources, R.drawable.icon_delete_highlight)
    }

    private val clearPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
    }

    private val textBounds = Rect()
    private val textBoundsNoUnit = Rect()
    private val sliceTextPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        textSize = ScreenUtil.dp2FloatPx(context, 10f)
        color = Color.WHITE
        textAlign = Paint.Align.LEFT
    }

    private val sliceBgPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        color = Color.parseColor("#80000000") // 半透明背景
    }

    private var timeChangeListener: ITimeChangeListener? = null

    private val sliceList = arrayListOf<HighLightMoment>()
    private val sliceTouchRegionMap = mutableMapOf<HighLightMoment, RectF>()
    private val sliceThumbTouchRegionMap = mutableMapOf<HighLightMoment, Pair<RectF, RectF>>()
    private var sliceListener: ISliceListener? = null
    private var clickSlice: HighLightMoment? = null
    private var centerPivotSlice: HighLightMoment? = null

    private var activeDragType = NONE
    private var activeDragSlice: HighLightMoment? = null
    private var initialDragX = 0f

    private var videoThumbnailLoader: VideoThumbnailLoader? = null

    constructor(ctx: Context) : this(ctx, null)

    constructor(ctx: Context, attrs: AttributeSet?) : this(ctx, attrs, 0)

    constructor(ctx: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(ctx, attrs, defStyleAttr) {
        init()
    }

    private fun init() {
        THUMBNAIL_WIDTH = ScreenUtil.dp2px(context, 30f)
        THUMBNAIL_HEIGHT = ScreenUtil.dp2px(context, 30f)
        mPaint.isAntiAlias = true
        mPaint.isDither = true

        mLinePaint.isAntiAlias = true
        mLinePaint.isDither = true
        mLinePaint.color = Color.parseColor("#000000")
        mLinePaint.style = Paint.Style.STROKE
        mLinePaint.strokeWidth = 2f

        mTextPaint.isAntiAlias = true
        mTextPaint.isDither = true
        mTextPaint.textSize = ScreenUtil.dp2FloatPx(context, 10f)
        mTextPaint.color = Color.parseColor("#FFFFFF")
        mTextPaint.textAlign = Paint.Align.LEFT
        val mTextRect = Rect()
        mTextPaint.getTextBounds("00:00", 0, "00:00".length, mTextRect)
        textHeight = mTextRect.height().toFloat() * 2

        pixelsPerSecond = THUMBNAIL_WIDTH.toFloat()
        thumbnailCount = 0f
        highLightActionWidth = (ScreenUtil.getScreenWidth(context) * 0.13f).roundToInt()
        visibleWidth = ScreenUtil.getScreenWidth(context) - highLightActionWidth

        radius = ScreenUtil.dp2FloatPx(context, 3f)
        offset = ScreenUtil.dp2px(context, 6f)
        offset = textHeight.toInt()

        checkedBitmapSize = ScreenUtil.dp2px(context, 10f)
        highLightBitmapSize = ScreenUtil.dp2px(context, 16f)
        dpSize4 = ScreenUtil.dp2px(context, 4f)

        setLayerType(LAYER_TYPE_HARDWARE, null)
        setWillNotDraw(false)
    }

    private fun printLog(msg: String) {
        Log.d(TAG, msg)
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        var width = MeasureSpec.getSize(widthMeasureSpec)
        if (thumbnailCount > 0) {
            width = (THUMBNAIL_WIDTH * thumbnailCount).roundToInt()
        }
        var height: Int = (THUMBNAIL_HEIGHT + textHeight).toInt()
        height += offset
        setMeasuredDimension(resolveSize(width, widthMeasureSpec), resolveSize(height, heightMeasureSpec))
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        printLog("w: $w, h: $h, oldw: $oldw, oldh: $oldh")
    }

    private fun requestRefresh(timeSec: Float) {
        // 检查是否仍然在可视区域内，再刷新
        val startSec = (scrollOffsetPx / pixelsPerSecond).toInt().coerceAtLeast(0)
        val endSec = startSec + (visibleWidth / pixelsPerSecond).roundToInt() + 1
        if (timeSec.roundToInt() !in startSec..endSec) {
            return
        }

        // 防抖：已有等待刷新，不重复发起
        if (refreshPending) {
            return
        }
        refreshPending = true
        refreshHandler.postDelayed({
            refreshPending = false
            postInvalidateOnAnimation()
        }, 8) // 延迟 8ms，防止频繁刷新
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        //绘制背景
        thumbnailRect.setEmpty()
        thumbnailRect.set(0f, 0f, width.toFloat(), height.toFloat())
        mPaint.style = Paint.Style.FILL
        mPaint.color = bgColor
        canvas.drawRect(thumbnailRect, mPaint)

        if (videoDurationSec < 1) {
            return
        }

        //绘制缩略图
        val firstThumbnailIndex = maxOf(0, (scrollOffsetPx / THUMBNAIL_WIDTH).toInt())
        val visibleThumbnailCount = ceil(visibleWidth.toFloat() / THUMBNAIL_WIDTH).toInt() + 3
        val lastThumbnailIndex = minOf(thumbnailCount.roundToInt() - 1, firstThumbnailIndex + visibleThumbnailCount)

        //printLog("----------------------------------, firstThumbnailIndex: $firstThumbnailIndex, lastThumbnailIndex: $lastThumbnailIndex")
        for (i in firstThumbnailIndex..lastThumbnailIndex) {
            val left = i * THUMBNAIL_WIDTH - scrollOffsetPx
            val timeSec = i * (videoDurationSec / thumbnailCount)
            val roundedTimeSec = timeSec.roundToInt()
            if (enableText) {
                canvas.drawText(roundedTimeSec.toString(), left, textHeight / 2, mTextPaint)
                canvas.drawLine(left, 0f, left, height.toFloat() - offset, mLinePaint)
            }

            thumbnailRect.setEmpty()
            thumbnailRect.set(left, textHeight, left + THUMBNAIL_WIDTH, height.toFloat() - offset)
            var bmp = videoThumbnailLoader?.loadMemoryCacheBitmap(roundedTimeSec.toFloat()) ?: placeholderBitmap
            var findInCache = true
            if (bmp == placeholderBitmap) {
                findInCache = false
                //val first = videoThumbnailLoader?.loadMemoryCacheBitmap(i-firstIndex)
                //if (first != null) {
                //    bmp = first
                //}
            }
            canvas.drawBitmap(bmp, null, thumbnailRect, mPaint)
            if (!findInCache) {
                //fling时只从磁盘中加载缩略图，只有滑动停止才从相机中加载
                //printLog("isFlinging: $isFlinging, timeSec: $timeSec, roundedTimeSec: $roundedTimeSec")
                if (isFlinging) {
                    videoThumbnailLoader?.loadDiskCacheBitmap(roundedTimeSec.toFloat())
                } else {
                    //printLog("loadBitmap: $timeSec")
                    videoThumbnailLoader?.loadBitmap(roundedTimeSec.toFloat(), false)
                }
            }
        }

        //绘制高光片段标记
        drawSliceList(canvas)

        //绘制动作栏
        drawHighLightAction(canvas)
    }

    override fun onFrameThumbLoaded(sec: Float, bitmap: Bitmap) {
        //printLog("onFrameThumbLoaded: $sec")
        refreshHandler.post {
            requestRefresh(sec)
        }
    }

    private fun drawHighLightAction(canvas: Canvas) {
        sliceTextPaint.textSize = ScreenUtil.dp2FloatPx(context, 8f)
        sliceTextPaint.color = Color.parseColor("#b3ffffff")
        val label = if (centerPivotSlice == null) "添加高光" else "删除高光"
        sliceTextPaint.getTextBounds(label, 0, label.length, textBounds)

        val h = highLightBitmapSize + dpSize4 + textBounds.height()
        thumbnailRect.setEmpty()
        thumbnailRect.set(
            highLightActionWidth / 2f - highLightBitmapSize / 2f,
            height / 2 - h / 2f,
            highLightActionWidth / 2f + highLightBitmapSize / 2,
            height / 2 - h / 2f + highLightBitmapSize.toFloat()
        )
        highLightClickRect.set(0f, 0f, highLightActionWidth.toFloat(), height.toFloat())

        mPaint.style = Paint.Style.FILL
        mPaint.color = bgColor
        canvas.drawRect(highLightClickRect, mPaint)

        canvas.drawBitmap(if (centerPivotSlice == null) addHighLightActionBitmap else deleteHighLightActionBitmap, null, thumbnailRect, mPaint)
        canvas.drawText(
            label,
            highLightActionWidth / 2 - textBounds.width() / 2f,
            height / 2f + h / 2f,
            sliceTextPaint
        )

        //绘制中心标记线
        mPaint.style = Paint.Style.FILL
        mPaint.color = Color.WHITE
        thumbnailRect.setEmpty()
        thumbnailRect.set(highLightActionWidth + visibleWidth / 2f - dpSize4 / 4f, 0f, highLightActionWidth + visibleWidth / 2f + dpSize4 / 4f, height.toFloat())
        canvas.drawRoundRect(thumbnailRect, dpSize4 / 4f, dpSize4 / 4f, mPaint)
    }

    private fun drawSliceList(canvas: Canvas) {
        if (sliceList.isEmpty()) {
            centerPivotSlice = null
            return
        }
        sliceTouchRegionMap.clear()
        sliceThumbTouchRegionMap.clear()
        val centerTime = getCenterPivotTime()
        centerPivotSlice = null
        for (slice in sliceList) {
            if (centerTime in slice.startTimestamp..slice.endTimestamp) {
                centerPivotSlice = slice
            }
            if (slice == clickSlice) {
                drawClickedSlice(canvas, slice)
            } else {
                drawNonClickedSlice(canvas, slice)
            }
        }
    }

    private fun drawNonClickedSlice(canvas: Canvas, slice: HighLightMoment) {
        val startPx = slice.startTimestamp * pixelsPerSecond - scrollOffsetPx
        val endPx = slice.endTimestamp * pixelsPerSecond - scrollOffsetPx
        val top = textHeight
        val bottom = height.toFloat() - offset

        // 绘制边框
        val rect = RectF(startPx, top, endPx, bottom)
        sliceBgPaint.style = Paint.Style.STROKE
        sliceBgPaint.strokeWidth = ScreenUtil.dp2FloatPx(context, 1f)
        sliceBgPaint.color = Color.WHITE
        canvas.drawRoundRect(rect, radius, radius, sliceBgPaint)

        // 绘制左上角背景
        var label = "${(slice.endTimestamp - slice.startTimestamp).roundToInt()}s"
        val labelNoUnit = "${(slice.endTimestamp - slice.startTimestamp).roundToInt()}"
        val padding = ScreenUtil.dp2px(context, 2f)
        sliceTextPaint.getTextBounds(label, 0, label.length, textBounds)
        sliceTextPaint.getTextBounds(labelNoUnit, 0, labelNoUnit.length, textBoundsNoUnit)
        val textRegionStart = startPx + padding
        val textRegionTop = top + padding
        var textRegionWidth = padding * 2 + (if (slice.checked) checkedBitmapSize + padding else 0) + textBounds.width()
        val textRegionWidthNoUnit = padding * 2 + (if (slice.checked) checkedBitmapSize + padding else 0) + textBoundsNoUnit.width()
        val textRegionHeight = padding * 2 + max(textBounds.height(), checkedBitmapSize)
        val sliceWidth = endPx - startPx - padding * 2
        if (sliceWidth > textRegionWidthNoUnit && sliceWidth < textRegionWidth) {
            textRegionWidth = textRegionWidthNoUnit
            label = labelNoUnit
        } else if (sliceWidth < textRegionWidthNoUnit) {
            textRegionWidth = 0
        }
        if (textRegionWidth <= 0) {
            return
        }
        rect.set(
            textRegionStart,
            textRegionTop,
            textRegionStart + textRegionWidth,
            textRegionTop + textRegionHeight
        )
        sliceBgPaint.style = Paint.Style.FILL
        sliceBgPaint.color = Color.parseColor("#80000000")
        canvas.drawRoundRect(rect, radius, radius, sliceBgPaint)
        //绘制选中图表
        if (slice.checked) {
            rect.set(textRegionStart + padding, textRegionTop + padding, textRegionStart + padding + checkedBitmapSize, textRegionTop + padding + checkedBitmapSize)
            canvas.drawBitmap(checkedBitmap, null, rect, sliceBgPaint)
        }
        // 绘制时间文本
        sliceTextPaint.textSize = ScreenUtil.dp2FloatPx(context, 10f)
        sliceTextPaint.color = Color.WHITE
        canvas.drawText(
            label,
            textRegionStart + padding + if (slice.checked) checkedBitmapSize + padding else 0,
            textRegionTop + textRegionHeight / 2 + textBounds.height() / 2,
            sliceTextPaint
        )

        // 更新点击区域记录
        sliceTouchRegionMap[slice] = RectF(startPx, top, endPx, bottom)
    }

    private fun drawClickedSlice(canvas: Canvas, slice: HighLightMoment) {
        val startPx = slice.startTimestamp * pixelsPerSecond - scrollOffsetPx
        val endPx = slice.endTimestamp * pixelsPerSecond - scrollOffsetPx
        val top = textHeight
        val bottom = height.toFloat() - offset

        val padding = ScreenUtil.dp2px(context, 2f)
        val thumbWidth = ScreenUtil.dp2px(context, 18f)
        val thumbLineWidth = ScreenUtil.dp2px(context, 2f)
        val thumbLineHeight = THUMBNAIL_HEIGHT / 2

        val layer = canvas.saveLayer(startPx - thumbWidth, top, endPx + thumbWidth, bottom, null)

        // 绘制边框
        val rect = RectF(startPx - thumbWidth, top, endPx + thumbWidth, bottom)
        sliceBgPaint.style = Paint.Style.FILL
        sliceBgPaint.color = Color.parseColor("#FFFFFF")
        canvas.drawRoundRect(rect, radius, radius, sliceBgPaint)

        //绘制滑块指示线
        sliceBgPaint.style = Paint.Style.STROKE
        sliceBgPaint.strokeWidth = thumbLineWidth.toFloat()
        sliceBgPaint.color = Color.parseColor("#C7C7C7")
        canvas.drawLine(startPx - thumbWidth / 2f, top + (bottom - top) / 2 - thumbLineHeight / 2, startPx - thumbWidth / 2f, top + (bottom - top) / 2 + thumbLineHeight / 2, sliceBgPaint)
        canvas.drawLine(endPx + thumbWidth / 2f, top + (bottom - top) / 2 - thumbLineHeight / 2, endPx + thumbWidth / 2f, top + (bottom - top) / 2 + thumbLineHeight / 2, sliceBgPaint)

        //挖一个透明矩形孔, 漏出下层缩略图
        canvas.drawRoundRect(RectF(startPx, top + padding, endPx, bottom - padding), radius, radius, clearPaint)

        //恢复图层，保留透明挖孔
        canvas.restoreToCount(layer)

        // 绘制左上角背景
        var label = "${(slice.endTimestamp - slice.startTimestamp).roundToInt()}s"
        val labelNoUnit = "${(slice.endTimestamp - slice.startTimestamp).roundToInt()}"
        sliceTextPaint.getTextBounds(label, 0, label.length, textBounds)
        val textRegionStart = startPx + padding
        val textRegionTop = top + padding + padding
        var textRegionWidth = padding * 2 + (if (slice.checked) checkedBitmapSize + padding else 0) + textBounds.width()
        val textRegionWidthNoUnit = padding * 2 + (if (slice.checked) checkedBitmapSize + padding else 0) + textBoundsNoUnit.width()
        val textRegionHeight = padding * 2 + max(textBounds.height(), checkedBitmapSize)
        val sliceWidth = endPx - startPx - padding * 2
        if (sliceWidth > textRegionWidthNoUnit && sliceWidth < textRegionWidth) {
            textRegionWidth = textRegionWidthNoUnit
            label = labelNoUnit
        } else if (sliceWidth < textRegionWidthNoUnit) {
            textRegionWidth = 0
        }
        if (textRegionWidth <= 0) {
            return
        }
        rect.set(
            textRegionStart,
            textRegionTop,
            textRegionStart + textRegionWidth,
            textRegionTop + textRegionHeight
        )
        sliceBgPaint.style = Paint.Style.FILL
        sliceBgPaint.color = Color.parseColor("#80000000")
        canvas.drawRoundRect(rect, radius, radius, sliceBgPaint)
        //绘制选中图表
        if (slice.checked) {
            rect.set(textRegionStart + padding, textRegionTop + padding, textRegionStart + padding + checkedBitmapSize, textRegionTop + padding + checkedBitmapSize)
            canvas.drawBitmap(checkedBitmap, null, rect, sliceBgPaint)
        }
        // 绘制时间文本
        sliceTextPaint.textSize = ScreenUtil.dp2FloatPx(context, 10f)
        sliceTextPaint.color = Color.WHITE
        canvas.drawText(
            label,
            textRegionStart + padding + if (slice.checked) checkedBitmapSize + padding else 0,
            textRegionTop + textRegionHeight / 2 + textBounds.height() / 2,
            sliceTextPaint
        )

        sliceThumbTouchRegionMap[slice] = Pair(RectF(startPx - thumbWidth, top, startPx, bottom), RectF(endPx, top, endPx + thumbWidth, bottom))
    }

    private fun findAdjacentRange(slice: HighLightMoment): Pair<HighLightMoment?, HighLightMoment?> {
        val index = sliceList.indexOf(slice)
        val left = if (index > 0) sliceList[index - 1] else null
        val right = if (index < sliceList.size - 1) sliceList[index + 1] else null
        return Pair(left, right)
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        if (videoDurationSec == 0f) {
            return false
        }
        if (event.pointerCount == 2) {
            scaleDetector.onTouchEvent(event)
        } else if (event.pointerCount == 1 && !isScaling) {
            val x = event.x
            val y = event.y
            when (event.action) {
                MotionEvent.ACTION_DOWN -> {
                    for ((slice, pair) in sliceThumbTouchRegionMap) {
                        val (leftRect, rightRect) = pair
                        if (leftRect.contains(x, y)) {
                            activeDragType = LEFT_THUMB
                            activeDragSlice = slice
                            initialDragX = x
                            parent.requestDisallowInterceptTouchEvent(true)
                            return true
                        } else if (rightRect.contains(x, y)) {
                            activeDragType = RIGHT_THUMB
                            activeDragSlice = slice
                            initialDragX = x
                            parent.requestDisallowInterceptTouchEvent(true)
                            return true
                        }
                    }
                    gestureDetector.onTouchEvent(event)
                }

                MotionEvent.ACTION_MOVE -> {
                    if (activeDragSlice != null && activeDragType != NONE) {
                        val deltaPx = x - initialDragX
                        val deltaSec = deltaPx / pixelsPerSecond
                        val (prev, next) = findAdjacentRange(activeDragSlice!!)

                        when (activeDragType) {
                            LEFT_THUMB -> {
                                val minLimit = prev?.endTimestamp ?: 0f
                                val maxStart = activeDragSlice!!.endTimestamp - 3f
                                val newStart = (activeDragSlice!!.startTimestamp + deltaSec)
                                    .coerceAtLeast(minLimit)
                                    .coerceAtMost(maxStart)
                                activeDragSlice!!.startTimestamp = newStart
                                activeDragSlice!!.duration = activeDragSlice!!.endTimestamp - activeDragSlice!!.startTimestamp
                                activeDragSlice!!.durationLeft = activeDragSlice!!.highLightSec - activeDragSlice!!.startTimestamp
                                activeDragSlice!!.durationRight = activeDragSlice!!.endTimestamp - activeDragSlice!!.highLightSec
                                sliceListener?.onSliceChanged(activeDragSlice!!)
                            }

                            RIGHT_THUMB -> {
                                val maxLimit = next?.startTimestamp ?: videoDurationSec
                                val minEnd = activeDragSlice!!.startTimestamp + 3f
                                val newEnd = (activeDragSlice!!.endTimestamp + deltaSec)
                                    .coerceAtLeast(minEnd)
                                    .coerceAtMost(maxLimit)
                                activeDragSlice!!.endTimestamp = newEnd
                                activeDragSlice!!.duration = activeDragSlice!!.endTimestamp - activeDragSlice!!.startTimestamp
                                activeDragSlice!!.durationLeft = activeDragSlice!!.highLightSec - activeDragSlice!!.startTimestamp
                                activeDragSlice!!.durationRight = activeDragSlice!!.endTimestamp - activeDragSlice!!.highLightSec
                                sliceListener?.onSliceChanged(activeDragSlice!!)
                            }

                            else -> {}
                        }
                        initialDragX = x
                        invalidate()
                        return true
                    } else {
                        gestureDetector.onTouchEvent(event)
                    }
                }

                MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                    activeDragType = NONE
                    activeDragSlice = null
                    gestureDetector.onTouchEvent(event)
                    if (hasDispatchedScrollStart && !hasDispatchedScrollEnd) {
                        timeChangeListener?.onEndScroll()
                        isScrolling = false
                        hasDispatchedScrollEnd = true
                        hasDispatchedScrollStart = false
                    }
                }
            }
        }
        if (event.action == MotionEvent.ACTION_UP || event.action == MotionEvent.ACTION_CANCEL) {
            if (!scroller.isFinished) invalidate()
        }

        return true
    }

    private fun getCenterPivotTime(): Float {
        val sec = (scrollOffsetPx + getCenterPivotX()) / pixelsPerSecond
        //printLog("sec: $sec, scrollOffsetPx: $scrollOffsetPx")
        return sec
    }

    private var lastTimeSec = -1f
    private fun notifyTimeChange(userDrag: Boolean) {
        timeChangeListener?.apply {
            val sec = getCenterPivotTime()
            if (sec == lastTimeSec) {
                return
            }
            lastTimeSec = sec
            onTimeChanged(sec, userDrag)
        }
    }

    override fun computeScroll() {
        if (scroller.computeScrollOffset()) {
            scrollOffsetPx = scroller.currX.toFloat()
            //printLog("computeScroll, scrollOffsetPx: $scrollOffsetPx")
            invalidate()
        } else {
            if (isFlinging) {
                isFlinging = false
                requestRefresh(getCenterPivotTime()) // fling 结束，主动刷新
            }
            notifyTimeChange(false)
        }
    }

    private inner class GestureListener : GestureDetector.SimpleOnGestureListener() {
        override fun onSingleTapUp(e: MotionEvent): Boolean {
            if (isScrolling) {
                return true
            }
            val x = e.x
            val y = e.y
            if (highLightClickRect.contains(x, y)) {
                if (centerPivotSlice == null) {
                    sliceListener?.addSlice(getCenterPivotTime())
                } else {
                    sliceListener?.deleteSlice(centerPivotSlice!!)
                }
                return true
            }
            for ((slice, rect) in sliceTouchRegionMap) {
                if (rect.contains(x, y)) {
                    sliceListener?.onSliceClicked(slice)
                    clickSlice = slice
                    invalidate()
                    return true
                }
            }
            clearClickedSlice()
            return false
        }

        override fun onDown(e: MotionEvent): Boolean {
            val now = System.currentTimeMillis()
            val inBlockPeriod = now - lastScaleEndTime < SCROLL_BLOCK_DURATION
            if (scaleDetector.isInProgress || isScaling || inBlockPeriod) {
                return false
            }
            if (!scroller.isFinished) scroller.forceFinished(true)
            return true
        }

        override fun onScroll(e1: MotionEvent?, e2: MotionEvent, dx: Float, dy: Float): Boolean {
            val now = System.currentTimeMillis()
            val inBlockPeriod = now - lastScaleEndTime < SCROLL_BLOCK_DURATION
            if (scaleDetector.isInProgress || isScaling || inBlockPeriod) {
                return false
            }
            if (!hasDispatchedScrollStart) {
                timeChangeListener?.onStartScroll()
                isScrolling = true
                hasDispatchedScrollStart = true
                hasDispatchedScrollEnd = false
            }
            //printLog("onScroll, scrollOffsetPx: $scrollOffsetPx")
            scrollOffsetPx += dx
            scrollOffsetPx = scrollOffsetPx.coerceIn(minScrollOffset(), maxScrollOffset())
            invalidate()
            notifyTimeChange(true)
            return true
        }

        override fun onFling(e1: MotionEvent?, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean {
            //return true
            val now = System.currentTimeMillis()
            val inBlockPeriod = now - lastScaleEndTime < SCROLL_BLOCK_DURATION
            if (scaleDetector.isInProgress || isScaling || inBlockPeriod) {
                return false
            }
            isFlinging = true
            scroller.fling(
                scrollOffsetPx.roundToInt(), 0,
                -velocityX.toInt(), 0,
                minScrollOffset().roundToInt(), maxScrollOffset().roundToInt(),
                0, 0
            )
            invalidate()
            return true
        }
    }

    private fun minScrollOffset(): Float {
        return -getCenterPivotX()
    }

    private fun maxScrollOffset(): Float {
        return max(minScrollOffset(), thumbnailCount * THUMBNAIL_WIDTH.toFloat() - getCenterPivotX())
    }

    private inner class ScaleListener : ScaleGestureDetector.SimpleOnScaleGestureListener() {
        override fun onScaleBegin(detector: ScaleGestureDetector): Boolean {
            isScaling = true
            return true
        }

        override fun onScaleEnd(detector: ScaleGestureDetector) {
            isScaling = false
            lastScaleEndTime = System.currentTimeMillis()
            //printLog("onScaleEnd, $lastScaleEndTime")
        }

        override fun onScale(detector: ScaleGestureDetector): Boolean {
            scale(detector)
            return true
        }
    }

    private fun getCenterPivotX() : Float {
        val res =  highLightActionWidth + visibleWidth / 2f
        return res
    }

    private fun scale(scaleDetector: ScaleGestureDetector) {
        if (videoDurationSec <= 5) {
            printLog("Duration <= 5s, ignore scale.")
            return
        }
        //printLog("scaleFactor: $scaleFactor")
        val scaleFactor = scaleDetector.scaleFactor
        val focusX: Float = getCenterPivotX() // 缩放中心点永远设置为缩略图轴中心点

        val prev = pixelsPerSecond
        zoomPivotTimeSec = (scrollOffsetPx + focusX) / pixelsPerSecond
        pixelsPerSecond *= scaleFactor
        val minThumbnailCount = 5
        val minPPS = (minThumbnailCount * THUMBNAIL_WIDTH.toFloat()) / videoDurationSec
        val maxPPS = THUMBNAIL_WIDTH.toFloat()
        pixelsPerSecond = clamp(pixelsPerSecond, minPPS, maxPPS)
        if (prev == pixelsPerSecond) {
            return
        }
        scrollOffsetPx = zoomPivotTimeSec * pixelsPerSecond - focusX
        val newThumbnailCount = (videoDurationSec * pixelsPerSecond) / THUMBNAIL_WIDTH
        thumbnailCount = newThumbnailCount
        postInvalidateOnAnimation()
    }

    fun getVideoDurationSec(): Float {
        return videoDurationSec
    }

    // Public control methods
    fun setVideoInfo(videoThumbnailLoader: VideoThumbnailLoader, durationSec: Float) {
        if (durationSec < 1) {
            return
        }
        this.videoThumbnailLoader = videoThumbnailLoader
        videoDurationSec = durationSec
        thumbnailCount = videoDurationSec
        pixelsPerSecond = THUMBNAIL_WIDTH.toFloat()
        printLog("videoDurationSec: $videoDurationSec, thumbnailCount: $thumbnailCount, pixelsPerSecond: $pixelsPerSecond")
        scrollOffsetPx = -getCenterPivotX()

        requestLayout()
    }

    fun setVideoSliceList(list: List<HighLightMoment>) {
        sliceList.clear()
        sliceList.addAll(list)
        invalidate()
    }

    fun scrollToTime(sec: Float) {
        val targetOffset = (sec * pixelsPerSecond - getCenterPivotX()).coerceIn(minScrollOffset(), maxScrollOffset())
        //printLog("targetOffset: $targetOffset")
        if (!scroller.isFinished) scroller.forceFinished(true)

        val startX = scrollOffsetPx.roundToInt()
        val dx = (targetOffset - startX).roundToInt()

        scrollOffsetPx = targetOffset
        //如果用Scroller滑动到指定时间，会把中间部分的时间也划过去，这些时间点会加入到抽帧列表中，影响最终时间点的加载耗时
        //scroller.startScroll(startX, 0, dx, 0, 200)
        postInvalidateOnAnimation()
    }

    fun setTimeChangeListener(listener: ITimeChangeListener) {
        timeChangeListener = listener
    }

    fun setSliceListener(listener: ISliceListener) {
        this.sliceListener = listener
    }

    fun clearClickedSlice() {
        clickSlice = null
        invalidate()
    }

    fun setClickSlice(slice: HighLightMoment) {
        clickSlice = slice
        postInvalidateOnAnimation()
    }

    interface ITimeChangeListener {
        fun onStartScroll()

        fun onEndScroll()

        fun onTimeChanged(timeSec: Float, userDrag: Boolean)
    }

    interface ISliceListener {
        fun onSliceClicked(slice: HighLightMoment)

        fun addSlice(startSec: Float)

        fun deleteSlice(slice: HighLightMoment)

        fun onSliceChanged(slice: HighLightMoment)
    }

    private enum class DragType {
        NONE, LEFT_THUMB, RIGHT_THUMB
    }
}
